package lego

import (
	"encoding/json"
	"fmt"
	"github.com/flosch/pongo2"
	"github.com/lestrrat-go/urlenc"
	"github.com/valyala/fasthttp"
	"io"
	"path"

	"github.com/mjiulee/lego/utils"
)

type RequestCtxExtent struct {
	*fasthttp.RequestCtx
}

/* 获取参数
* @parsm
	code --- 错误码
	data --- 数据
*/
func (self *RequestCtxExtent) Peek(key string) []byte {
	if self.IsPost() {
		return self.PostArgs().Peek(key)
	} else {
		return self.QueryArgs().Peek(key)
	}
}

/* 输出json
* @parsm
	code --- 错误码
	data --- 数据
*/
func (self *RequestCtxExtent) JSON(code int, dataMap map[string]interface{}) {
	str, err := json.Marshal(dataMap)
	if err != nil {
		self.Response.SetStatusCode(505)
		self.Write([]byte("系统错误"))
	} else {
		//这里改为json后，前端会自动parse，会有long溢出问题
		self.Response.Header.Set("Content-type", "application/json")
		self.Response.SetStatusCode(code)

		self.Write(str)
	}
}

/* 设置返回消息
* @parsm
	code --- 错误码
	data --- 数据
*/
func (self *RequestCtxExtent) ResponseWithStateCode200(code int, msg string) {
	ctxExt := self
	data := make(map[string]interface{})
	data["code"] = code
	data["msg"] = msg
	ctxExt.JSON(200, data)
}

/* 输出xml
* @parsm
	code --- 错误码
	data --- 数据
*/
func (self *RequestCtxExtent) XML(dataMap map[string]string) {
	str := utils.Map2Xml(dataMap)
	self.Response.SetStatusCode(200)
	self.Write([]byte(str))
}

/* ******************************************** */
/* pongo2 模板支持，定义rander类，以做界面模板显示处理 */

/* HTML
* @parsm
	name --- 前端模板文件的名称
	data --- 数据
*/
func (self *RequestCtxExtent) HTML(name string, dataMap map[string]interface{}) {
	defer func() {
		if err := recover(); err != nil {
			LogError(err)
		}
	}()

	render := getRender()
	render.Template = render.gettmpl(name)

	self.SetContentType(render.Options.ContentType)
	err := render.html(dataMap, self.Response.BodyWriter())
	if err != nil {
		LogError(err)
	}
}

func (self *RequestCtxExtent) UrlPeek(key string) []byte {
	val := self.RequestCtx.Request.URI().QueryArgs().Peek(key)
	return val
}

func (self *RequestCtxExtent) UrlDecode(urlParam interface{}) error {
	queryString := self.Request.URI().QueryArgs().QueryString()
	if err := urlenc.Unmarshal([]byte(queryString), urlParam); err != nil {
		return err
	}

	return nil
}

/* HTML
* @parsm
	name --- Wap端渲染
	data --- 数据
*/
func (self *RequestCtxExtent) WAPHTML(name string, dataMap map[string]interface{}) {
	defer func() {
		if err := recover(); err != nil {
			LogError(err)
		}
	}()

	render := getWapRender()
	render.Template = render.gettmpl(name)

	self.SetContentType(render.Options.ContentType)
	err := render.html(dataMap, self.Response.BodyWriter())
	if err != nil {
		LogError(err)
	}
}

/* HTML
* @parsm
	name --- Web端渲染
	data --- 数据
*/
func (self *RequestCtxExtent) WEBHTML(name string, dataMap map[string]interface{}) {
	defer func() {
		if err := recover(); err != nil {
			LogError(err)
		}
	}()

	render := getWebRender()
	render.Template = render.gettmpl(name)

	self.SetContentType(render.Options.ContentType)
	err := render.html(dataMap, self.Response.BodyWriter())
	if err != nil {
		LogError(err)
	}
}

/* html模板rander的一些显示配置 */
type Options struct {
	TemplateDir string
	ContentType string
}

type Render struct {
	Options  *Options
	Template *pongo2.Template
	Context  pongo2.Context
}

/* 通过文件名称和数据，load取pongo2的模板文件并输出给浏览器
* @parsm
	name --- 前端模板文件的名称
*/
func (render *Render) gettmpl(name string) *pongo2.Template {
	filename := path.Join(render.Options.TemplateDir, name)
	return pongo2.Must(pongo2.FromFile(filename))
}

/* 输出给浏览器
* @parsm
	data --- 输出给模板的数据
*/
func (render *Render) html(data interface{}, w io.Writer) error {
	render.Context = pongo2.Context{
		"data":            data,
		"GoFloatWrap":     GoFloatWrap,
		"ExtentIntString": ExtentIntString,
	}
	err := render.Template.ExecuteWriter(render.Context, w)
	return err
}

/* 全局静态rander变量，调用请通过getRender
 * @parsm
 */
var _rander *Render
var _waprander *Render

func getRender() *Render {
	if nil != _rander {
		return _rander
	}
	prjpath := utils.GetPwd()
	return &Render{Options: &Options{
		TemplateDir: prjpath + utils.GetPathSeparter() + "modules",
		ContentType: "text/html; charset=utf-8",
	}}
}

func getWapRender() *Render {
	if nil != _waprander {
		return _waprander
	}
	prjpath := utils.GetPwd()
	return &Render{Options: &Options{
		TemplateDir: prjpath + utils.GetPathSeparter() + "modules",
		ContentType: "text/html; charset=utf-8",
	}}
}

func getWebRender() *Render {
	if nil != _waprander {
		return _waprander
	}
	prjpath := utils.GetPwd()
	return &Render{Options: &Options{
		TemplateDir: prjpath + utils.GetPathSeparter() + "frontend",
		ContentType: "text/html; charset=utf-8",
	}}
}

func GoFloatWrap(val float64, jd int) string {
	fstr := "%0." + fmt.Sprintf("%d", jd) + "f"
	fmt.Println(fstr)
	return fmt.Sprintf(fstr, val)
}

func ExtentIntString(val int64) string {
	return fmt.Sprintf("%0d", val)
}
